/**
    @author: dt
    @date: 2025-01-23
    @description: 定点数2D向量实现
*/

using System;
using UnityEngine;

namespace DLib.FPMath
{
    /// <summary>
    /// 定点数2D向量
    /// </summary>
    [Serializable]
    public struct FPVec2 : IEquatable<FPVec2>
    {
        public FPNumber x;
        public FPNumber y;

        #region 构造函数

        public FPVec2(FPNumber x, FPNumber y)
        {
            this.x = x;
            this.y = y;
        }

        public FPVec2(FPNumber value)
        {
            this.x = value;
            this.y = value;
        }

        public FPVec2(Vector2 vector)
        {
            this.x = new FPNumber(vector.x);
            this.y = new FPNumber(vector.y);
        }

        #endregion

        #region 静态常量

        public static FPVec2 Zero => new FPVec2(FPNumber.Zero, FPNumber.Zero);
        public static FPVec2 One => new FPVec2(FPNumber.One, FPNumber.One);
        public static FPVec2 Up => new FPVec2(FPNumber.Zero, FPNumber.One);
        public static FPVec2 Down => new FPVec2(FPNumber.Zero, -FPNumber.One);
        public static FPVec2 Left => new FPVec2(-FPNumber.One, FPNumber.Zero);
        public static FPVec2 Right => new FPVec2(FPNumber.One, FPNumber.Zero);

        #endregion

        #region 属性

        public FPNumber Magnitude => FPNumber.Sqrt(SqrMagnitude);

        public FPNumber SqrMagnitude => x * x + y * y;

        public FPVec2 Normalized => Magnitude > FPNumber.Zero ? this / Magnitude : Zero;

        public bool IsZero => x.IsZero && y.IsZero;

        public FPNumber this[int index]
        {
            get
            {
                return index switch
                {
                    0 => x,
                    1 => y,
                    _ => throw new IndexOutOfRangeException("FPVec2 index out of range")
                };
            }
            set
            {
                switch (index)
                {
                    case 0: x = value; break;
                    case 1: y = value; break;
                    default: throw new IndexOutOfRangeException("FPVec2 index out of range");
                }
            }
        }

        #endregion

        #region 基本运算

        public static FPVec2 operator +(FPVec2 a, FPVec2 b)
        {
            return new FPVec2(a.x + b.x, a.y + b.y);
        }

        public static FPVec2 operator -(FPVec2 a, FPVec2 b)
        {
            return new FPVec2(a.x - b.x, a.y - b.y);
        }

        public static FPVec2 operator -(FPVec2 a)
        {
            return new FPVec2(-a.x, -a.y);
        }

        public static FPVec2 operator *(FPVec2 a, FPNumber b)
        {
            return new FPVec2(a.x * b, a.y * b);
        }

        public static FPVec2 operator *(FPNumber a, FPVec2 b)
        {
            return new FPVec2(a * b.x, a * b.y);
        }

        public static FPVec2 operator /(FPVec2 a, FPNumber b)
        {
            return new FPVec2(a.x / b, a.y / b);
        }

        public static bool operator ==(FPVec2 a, FPVec2 b)
        {
            return a.x == b.x && a.y == b.y;
        }

        public static bool operator !=(FPVec2 a, FPVec2 b)
        {
            return !(a == b);
        }

        #endregion

        #region 向量运算

        /// <summary>
        /// 点积
        /// </summary>
        public static FPNumber Dot(FPVec2 a, FPVec2 b)
        {
            return a.x * b.x + a.y * b.y;
        }

        /// <summary>
        /// 叉积（返回标量，表示z分量）
        /// </summary>
        public static FPNumber Cross(FPVec2 a, FPVec2 b)
        {
            return a.x * b.y - a.y * b.x;
        }

        /// <summary>
        /// 距离
        /// </summary>
        public static FPNumber Distance(FPVec2 a, FPVec2 b)
        {
            return (a - b).Magnitude;
        }

        /// <summary>
        /// 平方距离
        /// </summary>
        public static FPNumber SqrDistance(FPVec2 a, FPVec2 b)
        {
            return (a - b).SqrMagnitude;
        }

        /// <summary>
        /// 角度（弧度）
        /// </summary>
        public static FPNumber Angle(FPVec2 a, FPVec2 b)
        {
            FPNumber dot = Dot(a.Normalized, b.Normalized);
            dot = FPNumber.Max(-FPNumber.One, FPNumber.Min(FPNumber.One, dot));
            return FPNumber.Acos(dot);
        }

        /// <summary>
        /// 有符号角度（弧度）
        /// </summary>
        public static FPNumber SignedAngle(FPVec2 a, FPVec2 b)
        {
            FPNumber angle = Angle(a, b);
            FPNumber sign = FPNumber.Sign(Cross(a, b));
            return angle * sign;
        }

        /// <summary>
        /// 反射向量
        /// </summary>
        public static FPVec2 Reflect(FPVec2 inDirection, FPVec2 inNormal)
        {
            FPNumber dot = Dot(inDirection, inNormal);
            return inDirection - 2 * dot * inNormal;
        }

        /// <summary>
        /// 投影
        /// </summary>
        public static FPVec2 Project(FPVec2 vector, FPVec2 onNormal)
        {
            FPNumber sqrMag = onNormal.SqrMagnitude;
            if (sqrMag < FPNumber.FromFloat(1e-6f))
                return Zero;
            
            FPNumber dot = Dot(vector, onNormal);
            return onNormal * (dot / sqrMag);
        }

        /// <summary>
        /// 拒绝（垂直于投影）
        /// </summary>
        public static FPVec2 Reject(FPVec2 vector, FPVec2 onNormal)
        {
            return vector - Project(vector, onNormal);
        }

        #endregion

        #region 插值函数

        /// <summary>
        /// 线性插值
        /// </summary>
        public static FPVec2 Lerp(FPVec2 a, FPVec2 b, FPNumber t)
        {
            return new FPVec2(
                FPNumber.Lerp(a.x, b.x, t),
                FPNumber.Lerp(a.y, b.y, t)
            );
        }

        /// <summary>
        /// 平滑插值
        /// </summary>
        public static FPVec2 SmoothLerp(FPVec2 a, FPVec2 b, FPNumber t)
        {
            return new FPVec2(
                FPNumber.SmoothLerp(a.x, b.x, t),
                FPNumber.SmoothLerp(a.y, b.y, t)
            );
        }

        /// <summary>
        /// 球形插值
        /// </summary>
        public static FPVec2 SphericalLerp(FPVec2 a, FPVec2 b, FPNumber t)
        {
            FPNumber angle = Angle(a, b);
            if (angle.IsZero)
                return a;

            FPNumber sinAngle = FPNumber.Sin(angle);
            FPNumber sinT = FPNumber.Sin(t * angle);
            FPNumber sin1T = FPNumber.Sin((FPNumber.One - t) * angle);

            return (a * sin1T + b * sinT) / sinAngle;
        }

        #endregion

        #region 实用函数

        /// <summary>
        /// 最小值
        /// </summary>
        public static FPVec2 Min(FPVec2 a, FPVec2 b)
        {
            return new FPVec2(
                FPNumber.Min(a.x, b.x),
                FPNumber.Min(a.y, b.y)
            );
        }

        /// <summary>
        /// 最大值
        /// </summary>
        public static FPVec2 Max(FPVec2 a, FPVec2 b)
        {
            return new FPVec2(
                FPNumber.Max(a.x, b.x),
                FPNumber.Max(a.y, b.y)
            );
        }

        /// <summary>
        /// 移动朝向目标
        /// </summary>
        public static FPVec2 MoveTowards(FPVec2 current, FPVec2 target, FPNumber maxDistanceDelta)
        {
            FPVec2 delta = target - current;
            FPNumber sqrDistance = delta.SqrMagnitude;

            if (sqrDistance <= maxDistanceDelta * maxDistanceDelta)
                return target;

            FPNumber distance = FPNumber.Sqrt(sqrDistance);
            return current + delta * (maxDistanceDelta / distance);
        }

        /// <summary>
        /// 旋转向量
        /// </summary>
        public static FPVec2 Rotate(FPVec2 vector, FPNumber angle)
        {
            FPNumber cos = FPNumber.Cos(angle);
            FPNumber sin = FPNumber.Sin(angle);
            return new FPVec2(
                vector.x * cos - vector.y * sin,
                vector.x * sin + vector.y * cos
            );
        }

        /// <summary>
        /// 从角度创建单位向量
        /// </summary>
        public static FPVec2 FromAngle(FPNumber angle)
        {
            return new FPVec2(FPNumber.Cos(angle), FPNumber.Sin(angle));
        }

        /// <summary>
        /// 获取向量的角度
        /// </summary>
        public FPNumber ToAngle()
        {
            return FPNumber.Atan2(y, x);
        }

        /// <summary>
        /// 限制向量长度
        /// </summary>
        public FPVec2 ClampMagnitude(FPNumber maxLength)
        {
            FPNumber sqrMag = SqrMagnitude;
            if (sqrMag <= maxLength * maxLength)
                return this;

            FPNumber mag = FPNumber.Sqrt(sqrMag);
            return this * (maxLength / mag);
        }

        /// <summary>
        /// 设置向量长度
        /// </summary>
        public FPVec2 SetMagnitude(FPNumber magnitude)
        {
            return Normalized * magnitude;
        }

        #endregion

        #region 类型转换

        public static implicit operator FPVec2(Vector2 vector) => new FPVec2(vector);
        public static explicit operator Vector2(FPVec2 vector) => new Vector2(vector.x.AsFloat, vector.y.AsFloat);

        #endregion

        #region IEquatable 实现

        public bool Equals(FPVec2 other)
        {
            return x == other.x && y == other.y;
        }

        public override bool Equals(object obj)
        {
            return obj is FPVec2 other && Equals(other);
        }

        public override int GetHashCode()
        {
            return HashCode.Combine(x, y);
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return $"({x}, {y})";
        }

        public string ToString(string format)
        {
            return $"({x.ToString(format)}, {y.ToString(format)})";
        }

        #endregion
    }
} 